﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;

namespace DeepCast.Location
{
    /// <summary>
    /// Location provider to get cell tower details from the Windows Mobile Radio Interface layer
    /// </summary>
    public class CellTowerLocationProvider
    {
        #region pInvoke
        public delegate void RILRESULTCALLBACK(uint dwCode, IntPtr hrCmdID, IntPtr lpData, uint cbData, uint dwParam);
        public delegate void RILNOTIFYCALLBACK(uint dwCode, IntPtr lpData, uint cbData, uint dwParam);
        public static IntPtr hRil;
        [StructLayout(LayoutKind.Explicit)]
        class RILCELLTOWERINFO
        {
            [FieldOffset(0)]
            uint dwSize;
            [FieldOffset(4)]
            uint dwParams;
            [FieldOffset(8)]
            public uint dwMobileCountryCode;
            [FieldOffset(12)]
            public uint dwMobileNetworkCode;
            [FieldOffset(16)]
            public uint dwLocationAreaCode;
            [FieldOffset(20)]
            public uint dwCellID;
            [FieldOffset(24)]
            uint dwBaseStationID;
            [FieldOffset(28)]
            uint dwBroadcastControlChannel;
            [FieldOffset(32)]
            uint dwRxLevel;
            [FieldOffset(36)]
            uint dwRxLevelFull;
            [FieldOffset(40)]
            uint dwRxLevelSub;
            [FieldOffset(44)]
            uint dwRxQuality;
            [FieldOffset(48)]
            uint dwRxQualityFull;
            [FieldOffset(52)]
            uint dwRxQualitySub;
            /* More minor interesting fields below */
        }

        [DllImport("ril.dll")]
        private static extern IntPtr RIL_Initialize(uint dwIndex, RILRESULTCALLBACK pfnResult, RILNOTIFYCALLBACK pfnNotify, uint dwNotificationClasses, uint dwParam, out IntPtr lphRil);
        [DllImport("ril.dll", EntryPoint = "RIL_GetCellTowerInfo")]
        private static extern IntPtr RIL_GetCellTowerInfo(IntPtr hRil);
        [DllImport("ril.dll", EntryPoint = "RIL_Hangup")]
        private static extern IntPtr RIL_Hangup(IntPtr hRil);
        [DllImport("ril.dll")]
        private static extern IntPtr RIL_Deinitialize(IntPtr hRil);

        #endregion

        private static RILCELLTOWERINFO _towerDetails;
        private static AutoResetEvent waithandle = new AutoResetEvent(false);
        private System.Windows.Forms.Timer _timer = new System.Windows.Forms.Timer();
        private CellTower _currentTower;

        /// <summary>
        /// Initializes a new instance of the <see cref="CellTowerLocationProvider"/> class.
        /// </summary>
        public CellTowerLocationProvider()
        {
            _timer.Interval = 5000; // 5 seconds
            _timer.Tick += _timer_Tick;
        }

        #region ILocationProvider Members

        /// <summary>
        /// Starts the timer.
        /// </summary>
        public void Start()
        {
            _timer.Enabled = true;
        }
        /// <summary>
        /// Stops the timer.
        /// </summary>
        public void Stop()
        {
            _timer.Enabled = false;
        }
        /// <summary>
        /// Gets the current location.
        /// </summary>
        /// <value>The current location.</value>
        public GeoLocation CurrentLocation
        {
            get
            {
                try
                {
                    return GoogleMapsCellService.GetLocation(GetCellTowerInfo());
                }
                catch // Not much else can be done
                {
                    return GeoLocation.Empty;
                }
            }
        }

        #endregion

        /// <summary>
        /// Handles the Tick event of the _timer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void _timer_Tick(object sender, EventArgs e)
        {
            // Get the current cell tower every time the timer ticks
            _currentTower = GetCellTowerInfo();
        }

        public static GeoLocation CurrentLocationCell()
        {
            try
            {
                return GoogleMapsCellService.GetLocation(GetCellTowerInfo());
            }
            catch // Not much else can be done
            {
                return GeoLocation.Empty;
            }
        }

        /// <summary>
        /// Gets the cell tower info from the.
        /// </summary>
        /// <returns></returns>
        public static CellTower GetCellTowerInfo()
        {
            IntPtr radioInterfaceLayerHandle = IntPtr.Zero;
            IntPtr radioResponseHandle = IntPtr.Zero;

            // Initialize the radio layer with a result callback parameter.
            radioResponseHandle = RIL_Initialize(1, new RILRESULTCALLBACK(CellDataCallback), null, 0, 0, out radioInterfaceLayerHandle);

            // The initialize API call will always return 0 if initialization is successful.
            if (radioResponseHandle != IntPtr.Zero)
            {
                return null;
            }

            // Qusery for the current tower data.
            radioResponseHandle = RIL_GetCellTowerInfo(radioInterfaceLayerHandle);

            // Wait for cell tower info to be returned since RIL_GetCellTowerInfo invokes the 
            // callback method asynchronously.
            waithandle.WaitOne();

            // Release the RIL handle
            RIL_Deinitialize(radioInterfaceLayerHandle);

            // Convert the raw tower data structre data into a CellTower object
            return new CellTower()
            {
                TowerId = Convert.ToInt32(_towerDetails.dwCellID),
                LocationAreaCode = Convert.ToInt32(_towerDetails.dwLocationAreaCode),
                MobileCountryCode = Convert.ToInt32(_towerDetails.dwMobileCountryCode),
                MobileNetworkCode = Convert.ToInt32(_towerDetails.dwMobileNetworkCode),
            };
        }

        /// <summary>
        /// Responds to the RIL GetCellTowerInfo callback when data is available.
        /// </summary>
        public static void CellDataCallback(uint dwCode, IntPtr hrCmdID, IntPtr lpData, uint cbData, uint dwParam)
        {
            // Refresh the current tower details
            _towerDetails = new RILCELLTOWERINFO();

            // Copy result returned from RIL into structure
            Marshal.PtrToStructure(lpData, _towerDetails);

            // notify caller function that we have a result
            waithandle.Set();
        }
    }
}
